#include "serial_port.h"
#include <iostream>
#include <cstring>

serial_port::serial_port(const std::string &port_name, int baud_rate, bool ctsrts)
    : port_name(port_name), baud_rate(baud_rate), ctsrts(ctsrts), fd(-1), running(false) {}

serial_port::~serial_port() {
    close_port();
}

void serial_port::open_port() {
    fd = open(port_name.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        throw std::runtime_error("Failed to open port");
    }

    configure_port();

    running = true;
    read_thread = std::thread(&serial_port::read_loop, this);
}

void serial_port::configure_port() {
    memset(&tty, 0, sizeof tty);
    if (tcgetattr(fd, &tty) != 0) {
        close(fd);
        throw std::runtime_error("Failed to get port attributes");
    }

    cfmakeraw(&tty);
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 5;
    tty.c_cflag |= (CLOCAL | CREAD);

    if (ctsrts) {
        tty.c_cflag |= CRTSCTS;
    } else {
        tty.c_cflag &= ~CRTSCTS;
    }

    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag &= ~CSTOPB;

    cfsetispeed(&tty, baud_rate);
    cfsetospeed(&tty, baud_rate);

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        close(fd);
        throw std::runtime_error("Failed to set port attributes");
    }
}

void serial_port::close_port() {
    running = false;
    if (read_thread.joinable()) {
        read_thread.join();
    }
    if (fd >= 0) {
        close(fd);
        fd = -1;
    }
}

void serial_port::write_data(const std::vector<uint8_t> &data) {
    if (fd < 0) {
        throw std::runtime_error("Port is not open");
    }
    ssize_t n = write(fd, data.data(), data.size());
    if (n != static_cast<ssize_t>(data.size())) {
        throw std::runtime_error("Failed to write data");
    }
}

void serial_port::set_data_callback(data_callback_t callback) {
    data_callback = callback;
}

void serial_port::read_loop() {
    fd_set readfds;
    std::vector<uint8_t> buffer(256);

    while (running) {
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);

        int select_result = select(fd + 1, &readfds, NULL, NULL, NULL);

        if (select_result > 0 && FD_ISSET(fd, &readfds)) {
            ssize_t n = read(fd, buffer.data(), buffer.size());
            if (n > 0) {
                buffer.resize(n);
                if (data_callback) {
                    data_callback(buffer);
                }
            } else {
                buffer.resize(256);  // 重置缓冲区大小
            }
        }
    }
}
